#pragma once 

/* 实现好友管理子服务部分的封装 */

#include <brpc/server.h>
#include <butil/logging.h>
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <brpc/channel.h>

#include "brpc.hpp"     //Rpc调用模块的封装
#include "etcd.hpp"     //服务注册模块封装
#include "logger.hpp"   //日志模块封装
#include "data_es.hpp"  //ES数据管理客户端封装
#include "mysql_friend_relation.hpp"   //好友关系表的ODB映射操作
#include "mysql_friend_apply.hpp"   //好友申请事件表的ODB映射操作
#include "mysql_chat_session.hpp"   //聊天会话表的ODB映射操作
#include "mysql_chat_session_member.hpp"   //聊天会话成员表的ODB映射操作

#include "utils.hpp"    //工具类接口
#include "icsearch.hpp"     //ES接口的二次封装

#include "user.pb.h"        //protobuf框架代码
#include "base.pb.h"        //protobuf框架代码
#include "friend.pb.h"      //protobuf框架代码
#include "message.pb.h"     //protobuf框架代码


namespace ChatSystem
{

    // 1. 创建rpc服务子类继承pb(protobuf)中的FriendService服务类，并实现内部的业务接口逻辑；
    class FriendServiceImpl : public ChatSystem::FriendService
    {
    public:
        FriendServiceImpl(const std::shared_ptr<elasticlient::Client>& es_client,
            const std::shared_ptr<odb::core::database>& mysql_db,
            const ServiceManager::ptr& channel_manager,
            const std::string& user_service_name, 
            const std::string& message_service_name)
            :_es_user(std::make_shared<ESUser>(es_client))
            ,_mysql_relation(std::make_shared<RelationTable>(mysql_db))
            ,_mysql_apply(std::make_shared<FriendApplyTable>(mysql_db))
            ,_mysql_chat_session(std::make_shared<ChatSessionTable>(mysql_db))
            ,_mysql_chat_session_member(std::make_shared<ChatSessionMemberTable>(mysql_db))
            ,_channel_manager(channel_manager)
            ,_user_service_name(user_service_name)
            ,_message_service_name(message_service_name)
        {}

        ~FriendServiceImpl(){}


        //获取好友列表的接口
        void GetFriendList(google::protobuf::RpcController* controller,
            const ::ChatSystem::GetFriendListReq* request,
            ChatSystem::GetFriendListRsp* response,
            google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到获取好友列表的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 获取请求中的用户ID
            std::string user_id = request->user_id();

            //2. 根据用户ID，从数据库的好友关系表中查询获取用户的好友ID
            auto friend_id_lists = _mysql_relation->friends(user_id);

            //3. 从用户子服务中批量获取用户信息
            std::unordered_map<std::string, UserInfo> user_lists;
            std::unordered_set<std::string> user_id_lists;
            for(const auto& id : friend_id_lists)
            {
                user_id_lists.insert(id);
            }
            bool ret = _GetUserInfo(request_id, user_id_lists, user_lists);
            if(ret == false)
            {
                LOG_DEBUG("{} - 从用户管理子服务获取多个用户信息失败！", request_id);
                return err_response("从用户管理子服务获取多个用户信息失败！");
            }

            //4. 组织响应，将好友列表返回给网关
            for(const auto& user : user_lists)
            {
                auto user_info = response->add_friend_list();
                user_info->CopyFrom(user.second);
            }
            response->set_success(true);
        }

        //删除好友的接口
        void FriendRemove(google::protobuf::RpcController* controller,
        const ::ChatSystem::FriendRemoveReq* request,
        ChatSystem::FriendRemoveRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到删除好友的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 取出请求中的删除者ID和被删除者ID
            std::string user_id = request->user_id();
            std::string peer_id = request->peer_id();

            //2. 从用户好友关系表中删除相关关系数据，
            bool ret = _mysql_relation->remove(user_id, peer_id);
            if(ret == false)
            {
                LOG_ERROR("{} - 从好友关系表中删除好友关系 {}:{} 时失败！", request_id, user_id, peer_id);
                return err_response("从好友关系表中删除好友关系时失败！");
            }
            //3. 从会话表中删除单聊会话同时从会话成员表中删除会话成员信息
            ret = _mysql_chat_session->remove(user_id, peer_id);
            if(ret == false)
            {
                LOG_ERROR("{} - 从聊天会话表中删除好友关系 {}:{} 时失败！", request_id, user_id, peer_id);
                return err_response("从聊天会话表中删除好友关系时失败！");
            }
            //4. 组织响应，返回给网关
            response->set_success(true);
        }

        //申请添加好友的接口
        void FriendAdd(google::protobuf::RpcController* controller,
        const ::ChatSystem::FriendAddReq* request,
        ChatSystem::FriendAddRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到申请添加好友的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            
            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 取出请求中的请求者ID，和被请求者ID
            std::string user_id = request->user_id();
            std::string peer_id = request->respondent_id();

            //2. 判断两人是否已经是好友
            bool ret = _mysql_relation->exists(user_id, peer_id);
            if(ret == true)
            {
                LOG_ERROR("{} - 已是好友关系 {}:{} , 无需再次申请好友！", request_id, user_id, peer_id);
                return err_response("已是好友关系, 无需再次申请好友！");
            }

            //3. 判断该用户是否已经申请过好友关系
            ret = _mysql_apply->exists(user_id, peer_id);
            if(ret == true)
            {
                LOG_ERROR("{} - 已经存在好友申请 {}:{} , 无需再次发起好友申请！", request_id, user_id, peer_id);
                return err_response("已经存在好友申请, 无需再次发起好友申请！");
            }

            //4. 向好友申请事件表中，新增申请信息
            std::string event_id = ChatSystem::Util::uuid();
            FriendApply friend_apply_event(event_id, user_id, peer_id);
            ret = _mysql_apply->insert(friend_apply_event);
            if(ret == false)
            {
                LOG_ERROR("{} - 向好友关系表中新增好友关系 {}:{} 时失败！", request_id, user_id, peer_id);
                return err_response("向好友关系表中新增好友关系时失败！");
            }

            //5. 组织响应，将事件ID信息响应给网关
            response->set_success(true);
            response->set_notify_event_id(event_id);
        }

        //好友申请的处理接口
        void FriendAddProcess(google::protobuf::RpcController* controller,
        const ::ChatSystem::FriendAddProcessReq* request,
        ChatSystem::FriendAddProcessRsp* response,
        google::protobuf::Closure* done) override
        {
            //当前是站在被申请者的视角来写这个代码
            
            LOG_DEBUG("收到好友申请的处理的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 取出请求中的申请人ID，和被申请人ID，以及处理结果
            std::string user_id = request->user_id();   //被申请者的用户ID
            std::string apply_user_id = request->apply_user_id();   //好友申请者的用户ID
            bool agree = request->agree();

            //2. 根据两人 ID 在申请事件表中查询判断是否存在申请事件
            bool ret  = _mysql_apply->exists(apply_user_id, user_id);
            if(ret == false)
            {
                LOG_ERROR("{} - 不存在好友申请 {}:{} 事件!", request_id, apply_user_id, user_id);
                return err_response("好友申请事件不存在！");
            }

            //3. 判断两人是否已经是好友（互相加好友的情况）
            ret = _mysql_relation->exists(user_id, apply_user_id);
            if(ret == true)
            {
                LOG_ERROR("{} - 已是好友关系 {}:{} , 无需再次进行好友的添加！", request_id, user_id, apply_user_id);
                return err_response("已是好友关系, 无需再次进行好友的添加！");
            }

            //4. 不管拒绝还是同意，删除申请事件表中的事件信息（该事件处理完毕）
            ret = _mysql_apply->remove(user_id, apply_user_id);
            if(ret == false)
            {
                LOG_ERROR("{} - 从数据库中删除好友申请事件 {}:{} 失败！", request_id, user_id, apply_user_id);
                return err_response("从数据库中删除好友申请事件失败！");
            }
            //5. 若同意申请，则向用户关系表中添加好友关系数据，向会话表中新增会话信息，向会话成员表中新增成员信息；若拒绝不需要处理
            std::string chat_session_id;    //没有同意好友申请，这个聊天会话ID就是一个空
            if(agree == true)
            {
                //向用户关系表中添加好友关系数据
                ret = _mysql_relation->insert(user_id, apply_user_id);
                if(ret == false)
                {
                    LOG_ERROR("{} - 往数据库中新增好友关系 {}:{} 失败！", request_id, user_id, apply_user_id);
                    return err_response("往数据库中新增好友关系失败！");
                }
                //向会话表中新增会话信息
                chat_session_id = Util::uuid();
                ChatSession chat_session(chat_session_id, "", ChatSessionType::SINGLE);    //单聊会话的名称就是好友的名称，这里可以给空
                ret = _mysql_chat_session->insert(chat_session);
                if(ret == false)
                {
                    LOG_ERROR("{} - 往数据库中新增会话信息 {} 失败！", request_id, chat_session_id);
                    return err_response("往数据库中新增会话信息失败！");
                }
                //向会话成员表中新增成员信息
                ret = _mysql_chat_session_member->append({{chat_session_id, user_id}, {chat_session_id, apply_user_id}});
                if(ret == false)
                {
                    LOG_ERROR("{} - 往数据库中新增会话成员信息 {} 失败！", request_id, chat_session_id);
                    return err_response("往数据库中新增会话信息失败！");
                }
            }

            //6. 组织响应，将新生成的会话 ID 响应给网关
            response->set_success(true);
            response->set_new_chat_session_id(chat_session_id);
        }

        //用户搜索的接口 --- 这个接口应该是放到用户管理子服务中来实现的，在添加好友之前先搜索一下这个用户是否存在，但是放在这里实现问题也不大，
        //后期可以将这个接口移动到用户管理子服务中去实现；
        void FriendSearch(google::protobuf::RpcController* controller,
        const ::ChatSystem::FriendSearchReq* request,
        ChatSystem::FriendSearchRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到好友搜索的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 取出请求中的用户ID，和搜索关键字(可能是用户ID，可能是手机号，也可能是昵称的一部分)
            std::string user_id = request->user_id();
            std::string search_key = request->search_key();

            //2. 根据用户ID，获取用户的好友ID列表；防止产生已经是自己的好友了还去进行好友添加的操作
            auto friend_id_lists = _mysql_relation->friends(user_id);

            //3. 根据关键字从ES 服务器中进行用户搜索，搜索的时候需要将关键字作为用户ID/手机号/昵称的搜索关键字进行搜索，
            //且需要根据自己的ID 和好友ID 过滤掉自己和自己的好友。
            std::unordered_set<std::string> user_id_lists;
            friend_id_lists.push_back(user_id);
            auto search_res = _es_user->search(search_key, friend_id_lists);
            if(search_res.empty())
            {
                LOG_ERROR("{} - 没有搜索到用户！", request_id);
                return err_response("没有搜索到用户！");
            }
            for(const auto& it : search_res)
            {
                user_id_lists.insert(it.user_id());
            }
            //4. 根据搜索到的用户ID, 从用户子服务中批量获取用户信息
            std::unordered_map<std::string, UserInfo> user_lists;
            bool ret = _GetUserInfo(request_id, user_id_lists, user_lists);
            if(ret == false)
            {
                LOG_DEBUG("{} - 从用户管理子服务获取多个用户信息失败！", request_id);
                return err_response("从用户管理子服务获取多个用户信息失败！");
            }
            //5. 组织响应，将搜索到的用户列表响应给网关
            for(const auto& user : user_lists)
            {
                auto user_info = response->add_user_info();
                user_info->CopyFrom(user.second);
            }
            response->set_success(true);
        }

        //获取一个待处理的好友申请列表 --- 进行好友申请的时候，可能对方不在线，等对方上线后需要获取待处理的好友申请列表
        void GetPendingFriendEventList(google::protobuf::RpcController* controller,
        const ::ChatSystem::GetPendingFriendEventListReq* request,
        ChatSystem::GetPendingFriendEventListRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到获取待处理好友申请列表的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            // 1.取出请求中的用户ID 
            std::string user_id = request->user_id();
            // 2.根据用户ID，从申请事件表获取待处理的好友申请数据
            auto res = _mysql_apply->applyUsers(user_id);
            // 3.根据申请人用户ID，从用户子服务中获取用户的详细信息
            std::unordered_set<std::string> user_id_lists;
            for(const auto& id : res)
            {
                user_id_lists.insert(id);
            }
            std::unordered_map<std::string, UserInfo> user_lists;
            if(!res.empty())
            {
                bool ret = _GetUserInfo(request_id, user_id_lists, user_lists);
                if(ret == false)
                {
                    LOG_DEBUG("{} - 从用户管理子服务获取多个用户信息失败！", request_id);
                    return err_response("从用户管理子服务获取多个用户信息失败！");
                }
            }
            // 4.组织响应，将申请事件列表响应给网关
            for(const auto& user : user_lists)
            {
                auto ev = response->add_event();
                ev->mutable_sender()->CopyFrom(user.second);
            }
            response->set_success(true);
        }

        //获取聊天会话列表的接口 --- 一个用户登录成功后，能够展示自己的历史聊天信息
        void GetChatSessionList(google::protobuf::RpcController* controller,
        const ::ChatSystem::GetChatSessionListReq* request,
        ChatSystem::GetChatSessionListRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到获取聊天会话列表请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 从请求中取出用户ID
            std::string user_id = request->user_id();
            //2. 从数据库中查询出用户的单聊会话列表---单聊会话中，对方的昵称就是会话名称，对方的头像就是会话头像，会话类型为单聊类型
            auto single_chat_session_lists = _mysql_chat_session->singleChatSession(user_id);
            //2.1 从单聊会话类别中，取出所有的好友ID，从用户子服务中获取用户信息
            std::unordered_set<std::string> user_id_lists;
            for(const auto& s : single_chat_session_lists)
            {
                user_id_lists.insert(s.friend_id);
            }
            std::unordered_map<std::string, UserInfo> user_lists;
            bool ret = _GetUserInfo(request_id, user_id_lists, user_lists);
            if(ret == false)
            {
                LOG_DEBUG("{} - 从用户管理子服务获取多个用户信息失败！", request_id);
                return err_response("从用户管理子服务获取多个用户信息失败！");
            }

            //2.2 设置响应会话信息，会话名称就是好友名称，会话头像就是好友头像；
            //3. 从数据库中查询出用户的群聊会话列表
            auto group_chat_session_lists = _mysql_chat_session->groupChatSession(user_id);
            
            //4. 根据所有的会话ID，从消息存储子服务中获取会话的最后一条信息；--- 在组织响应的时候直接设置
            //5. 组织响应，响应给网关
            //单聊会话信息的设置
            for(const auto& s : single_chat_session_lists)
            {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_single_chat_friend_id(s.friend_id);
                chat_session_info->set_chat_session_id(s.chat_session_id);
                chat_session_info->set_chat_session_name(user_lists[s.friend_id].nickname());
                chat_session_info->set_avatar(user_lists[s.friend_id].avatar());
                MessageInfo msg;
                ret = _GetRecentMsg(request_id, s.chat_session_id, msg);
                if(ret == false)
                {
                    LOG_DEBUG("{} - 从消息存储子服务获取聊天会话的最后一条信息失败！", request_id);
                    return err_response("从消息存储子服务获取聊天会话的最后一条信息失败！");
                }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
            }

            //群聊会话信息的设置
            for(const auto& g : group_chat_session_lists)
            {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_chat_session_id(g.chat_session_id);
                chat_session_info->set_chat_session_name(g.chat_session_name);
                MessageInfo msg;
                ret = _GetRecentMsg(request_id, g.chat_session_id, msg);
                if(ret == false)
                {
                    LOG_DEBUG("{} - 从消息存储子服务获取聊天会话的最后一条信息失败！", request_id);
                    return err_response("从消息存储子服务获取聊天会话的最后一条信息失败！");
                }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
            }
            response->set_success(true);
        }

        //创建聊天会话的接口 --- 单聊会话是同意好友申请的时候创建的, 这里创建的是群聊的聊天会话；用户主动的发起一个创建群聊会话的请求
        void ChatSessionCreate(google::protobuf::RpcController* controller,
        const ::ChatSystem::ChatSessionCreateReq* request,
        ChatSystem::ChatSessionCreateRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到创建聊天会话的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 从请求中取出用户ID与会话名称，以及会话的成员ID列表
            std::string user_id = request->user_id();
            std::string chat_session_name = request->chat_session_name();
            
            //2. 生成会话ID，并向会话表中新增会话信息数据，会话为群聊会话（单聊会话是同意好友申请的时候创建的）
            std::string chat_session_id = Util::uuid();
            ChatSession chat_session(chat_session_id, chat_session_name, ChatSessionType::GROUP);
            bool ret = _mysql_chat_session->insert(chat_session);
            if(ret == false)
            {
                LOG_ERROR("{} - 往数据库中新增会话信息 {} 失败！", request_id, chat_session_id);
                return err_response("往数据库中新增会话信息失败！");
            }

            //3. 向会话成员表中新增所有的成员信息
            std::vector<ChatSessionMember> member_id_lists;
            for(int i = 0; i < request->member_id_list_size(); ++i)
            {
                ChatSessionMember chat_session_member(chat_session_id, request->member_id_list(i));
                member_id_lists.push_back(chat_session_member);
            }
            ret = _mysql_chat_session_member->append(member_id_lists);
            if(ret == false)
            {
                LOG_ERROR("{} - 往数据库中新增会话成员信息 {} 失败！", request_id, chat_session_id);
                return err_response("往数据库中新增会话信息失败！");
            }
            
            //4. 组织响应，将组织好的会话信息响应给网关。
            response->mutable_chat_session_info()->set_chat_session_id(chat_session_id);
            response->mutable_chat_session_info()->set_chat_session_name(chat_session_name);
            response->set_success(true);
        }

        //获取聊天会话成员列表的接口 --- 用于一个用户查看群聊成员信息的时候，进行群聊成员信息的展示
        void GetChatSessionMember(google::protobuf::RpcController* controller,
        const ::ChatSystem::GetChatSessionMemberReq* request,
        ChatSystem::GetChatSessionMemberRsp* response,
        google::protobuf::Closure* done) override
        {
            LOG_DEBUG("收到获取聊天会话成员列表的请求！");
            //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
            brpc::ClosureGuard rpc_gurd(done);
            //定义一个错误处理函数，当出错的时候被调用
            auto err_response = [this, response](const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            std::string request_id = request->request_id();
            response->set_request_id(request_id);

            //1. 取出请求中用户ID，和会话ID
            std::string user_id = request->user_id();
            std::string chat_session_id = request->chat_session_id();

            //2. 根据会话ID，从数据库获取会话成员ID列表
            auto member_id_lists = _mysql_chat_session_member->members(chat_session_id);

            //3. 从用户子服务批量获取用户信息
            std::unordered_set<std::string> user_id_lists;
            for(const auto& id : member_id_lists)
            {
                user_id_lists.insert(id);
            }
            std::unordered_map<std::string, UserInfo> user_lists;
            bool ret = _GetUserInfo(request_id, user_id_lists, user_lists);
            if(ret == false)
            {
                LOG_DEBUG("{} - 从用户管理子服务获取多个用户信息失败！", request_id);
                return err_response("从用户管理子服务获取多个用户信息失败！");
            }

            //4. 组织响应，将会话的成员用户信息列表响应给网关
            for(const auto& user : user_lists)
            {
                auto user_info = response->add_member_info_list();
                user_info->CopyFrom(user.second);
            }
            response->set_success(true);
        }

    private:
    //从用户管理子服务中获取用户信息
    bool _GetUserInfo(const std::string& request_id, 
        const std::unordered_set<std::string>& user_id_lists, 
        std::unordered_map<std::string, UserInfo>& user_lists)
    {
        auto channel = _channel_manager->choose(_user_service_name);
        if(!channel)
        {
            LOG_ERROR("{} - 未找到用户管理子服务节点 - {}!", request_id, _user_service_name);
            return false;
        }
        ChatSystem::UserService_Stub stub(channel.get());
        ChatSystem::GetMultiUserInfoReq req;
        req.set_request_id(request_id);
        for(const auto& id : user_id_lists)
        {
            req.add_users_id(id);
        }
        
        brpc::Controller cntl;
        ChatSystem::GetMultiUserInfoRsp rsp;
        stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);
        if(cntl.Failed() || rsp.success() == false)
        {
            LOG_ERROR("{} - 用户管理子服务 {} Rpc调用失败, {}!", request_id, _user_service_name, cntl.ErrorText());
            return false;
        }
        const auto& user_info_map = rsp.users_info();

        for(auto it = user_info_map.begin(); it != user_info_map.end(); ++it)
        {
            user_lists.insert(std::make_pair(it->first, it->second));     
        }
        return true;
    }

    //从消息存储子服务中获取最近的一条消息(最新消息)
    bool _GetRecentMsg(const std::string& request_id, 
        const std::string& chat_session_id, 
        MessageInfo& msg)
    {
        auto channel = _channel_manager->choose(_message_service_name);
        if(!channel)
        {
            LOG_ERROR("{} - 未找到消息存储子服务节点 - {}!", request_id, _message_service_name);
            return false;
        }
        ChatSystem::MsgStorageService_Stub stub(channel.get());
        ChatSystem::GetRecentMsgReq req;
        req.set_request_id(request_id);
        req.set_chat_session_id(chat_session_id);
        req.set_msg_count(1);
        
        brpc::Controller cntl;
        ChatSystem::GetRecentMsgRsp rsp;
        stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);
        if(cntl.Failed() || rsp.success() == false)
        {
            LOG_ERROR("{} - 消息存储子服务 {} Rpc调用失败, {}!", request_id, _message_service_name, cntl.ErrorText());
            return false;
        }
        if(rsp.msg_list_size() > 0)
        {
            msg = rsp.msg_list(0);
        }
        return true;
    }
        
    private:
        //这些是由内部构造的
        ChatSystem::RelationTable::ptr _mysql_relation;     //MySQL的relation表， 一定是先实例化odb操作句柄，在实例化这个，注意这个顺序；
        ChatSystem::FriendApplyTable::ptr _mysql_apply; //MySQL的friend_apply表
        ChatSystem::ChatSessionTable::ptr _mysql_chat_session; //MySQL的chat_session表
        ChatSystem::ChatSessionMemberTable::ptr _mysql_chat_session_member; //MySQL的chat_session_member表
        std::shared_ptr<ESUser> _es_user;     //ES的用户索引

        //这些是Rpc调用客户端相关对象
        std::string _message_service_name; //消息存储子服务名称
        std::string _user_service_name; //用户管理子服务名称
        ServiceManager::ptr _channel_manager;    //Rpc调用中信道管理对象

    };


    //好友管理子服务器搭建的流程类
    class FriendServer
    {
    public:
        using ptr = std::shared_ptr<FriendServer>;

        FriendServer(const Discovery::ptr& discovery_client, 
            const Registry::ptr& registry_client, 
            const std::shared_ptr<elasticlient::Client>& es_client,
            const std::shared_ptr<odb::core::database>& mysql_client,
            const std::shared_ptr<brpc::Server>& rpc_server)
        :_discovery_client(discovery_client)
        , _registry_client(registry_client)
        ,_es_client(es_client)
        ,_mysql_client(mysql_client)
        , _rpc_server(rpc_server)
        {}
        ~FriendServer(){}

        //搭建RPC服务器，并启动服务器
        /* make相关接口的调用必须在start接口调用之前，
        因为start涉及到了启动服务器，而启动服务器的前提是客户端对象和服务注册客户端对象已经实例化好了; 
        为了防止使用者在start接口调用之前未调用 make相关接口而导致
        服务器无法启动的问题，我们可以用建造者模式，将构造过程进行控制，在代码中规定调用顺序，去倒逼使用者;
        使用建造者模式的原因还是由于构造friendServer对象时参数太多，为了简化friendServer对象的构造 */
        void start()
        {
            // 阻塞等待服务端运行结束
            _rpc_server->RunUntilAskedToQuit();
        }
    private:
        Registry::ptr _registry_client;  //注册中心客户端
        Discovery::ptr _discovery_client;    //服务发现客户端
        std::shared_ptr<elasticlient::Client> _es_client;    //ES客户端
        std::shared_ptr<odb::core::database> _mysql_client;    //MySQL的ODB操作句柄
        std::shared_ptr<brpc::Server> _rpc_server;   //brpc服务器
    };

    //建造者模式---用来构造FriendServer对象，将FriendServer对象的真正构造过程给封装起来，通过封装的类去构造 FriendServer 对象
    class FriendServerBuilder
    {
    public:
        //构造es客户端对象
        void make_es_object(const std::vector<std::string>& host_list)
        {
            _es_client = EsClientFactory::create(host_list);
        }
        //构造mysql客户端对象
        void make_mysql_object(const std::string& user,
            const std::string& password,
            const std::string& host,
            const std::string& db,
            const std::string& cset,
            int port,
            int connection_pool_count)
        {
           _mysql_client = ODBFactory::create(user, password, host, db, cset, port, connection_pool_count);
        }

        //构造服务发现客户端对象和信道管理对象
        void make_discovery_object(const std::string& reg_host,
            const std::string& base_service_name,
            const std::string& message_service_name,
            const std::string& user_service_name)
        {
            _message_service_name = message_service_name;
            _user_service_name = user_service_name;
            _channel_manager = std::make_shared<ServiceManager>();
            _channel_manager->declared(_message_service_name);
            _channel_manager->declared(_user_service_name);
            LOG_DEBUG("设置用户管理子服务为需要添加管理的子服务：{}", _user_service_name);
            LOG_DEBUG("设置消息存储子服务为需要添加管理的子服务：{}", _message_service_name);
            auto put_cb = std::bind(&ChatSystem::ServiceManager::onServiceOnline, 
                _channel_manager.get(), 
                std::placeholders::_1, 
                std::placeholders::_2);
            auto del_cb = std::bind(&ChatSystem::ServiceManager::onServiceOffline, 
                _channel_manager.get(), 
                std::placeholders::_1,
                std::placeholders::_2);
            //2. 构造服务发现对象 
            _discovery_client = std::make_shared<ChatSystem::Discovery>(reg_host, base_service_name, put_cb, del_cb);
        }

        //用于构建RPC服务器
        void make_rpc_server(uint16_t port, 
            uint32_t timeout,      //连接空闲超时时间---超时后连接关闭
            uint8_t num_threads)    //rpc服务器的线程数量---IO线程数量；
        {
            _rpc_server = std::make_shared<brpc::Server>();
            // _es_client 的实例化必须在 user_service之前进行
            if(!_es_client)
            {
                LOG_ERROR("还未初始化ES搜索模块！");
                abort();
            }
            if(!_mysql_client)
            {
                LOG_ERROR("还未初始化MySQL数据库模块！");
                abort();
            }
            if(!_channel_manager)
            {
                LOG_ERROR("还未初始化信道管理模块！");
                abort();
            }
            if(!_discovery_client)
            {
                LOG_ERROR("还未初始化服务发现模块！");
                abort();
            }
            // 向rpc服务器类中添加rpc子服务对象---告诉服务器收到什么请求用哪个接口处理；
            /*注意：如果在friend_service对象是栈上创建的，AddService的第一个参数就是friend_service对象的地址，当
            make_rpc_server函数调用结束后friend_service这个对象是被销毁了的，而在 _rpc_server中会使用到 friend_service 对象
            此时就会出现段错误，所以我们这里需要在堆上创建对象，这样make_rpc_server函数调用结束后再使用friend_service对象也不会错。*/
            FriendServiceImpl* friend_service = new FriendServiceImpl(_es_client, 
                _mysql_client, 
                _channel_manager, 
                _user_service_name,
                _message_service_name);
            /* SERVER_DOESNT_OWN_SERVICE 表示添加服务失败时，服务器也不会删除服务器对象； SERVER_OWNS_SERVICE 表示添加服务失败时，
            服务器将负责删除服务对象，当我们在堆上创建friend_service对象时需要使用SERVER_OWNS_SERVICE，就不会造成内存泄漏。 */
            int ret = _rpc_server->AddService(friend_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE); 
            if(ret == -1)
            {
                LOG_ERROR("添加Rpc服务失败！ ");
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec - timeout;
            options.num_threads = num_threads;
            ret = _rpc_server->Start(port, &options);
            if(ret == -1)
            {
                LOG_ERROR("服务器启动失败！");
                abort();
            }
        }

         //用于构造服务注册客户端对象
        void make_registry_object(const std::string& reg_host,  //注册中心的地址
            const std::string& service_name,    //服务名称
            const std::string& access_host)     //提供服务器的主机的地址信息
        {
            if(!_rpc_server)
            {
                LOG_ERROR("还未初始化RPC服务器！");
                abort();
            }
            _registry_client = std::make_shared<Registry>(reg_host);
            _registry_client->registry(service_name, access_host);
        }

        //构造一个 FriendServer 对象
        FriendServer::ptr build()
        {
            if(!_es_client)
            {
                LOG_ERROR("还未初始化ES搜索模块！");
                abort();
            }
            if(!_mysql_client)
            {
                LOG_ERROR("还未初始化MySQL数据库模块！");
                abort();
            }
            if(!_channel_manager)
            {
                LOG_ERROR("还未初始化信道管理模块！");
                abort();
            }
            if(!_discovery_client)
            {
                LOG_ERROR("还未初始化服务发现模块！");
                abort();
            }
            if(!_registry_client)
            {
                LOG_ERROR("还未初始化服务注册模块");
                abort();
            }
            if(!_rpc_server)
            {
                LOG_ERROR("还未初始化RPC服务器模块");
                abort();
            }

            FriendServer::ptr server = std::make_shared<FriendServer>(_discovery_client, 
                _registry_client, 
                _es_client,
                _mysql_client,
                _rpc_server);
            return server;
        }
    private:
        std::string _message_service_name;  //消息存储子服务名称
        std::string _user_service_name;  //用户管理子服务名称

        std::shared_ptr<elasticlient::Client> _es_client;   //ES客户端
        std::shared_ptr<odb::core::database> _mysql_client; //MySQL的ODB操作句柄

        ServiceManager::ptr _channel_manager;   //信道管理对象
        Registry::ptr _registry_client;  //注册中心客户端
        Discovery::ptr _discovery_client;    //服务发现客户端对象

        std::shared_ptr<brpc::Server> _rpc_server;   //brpc服务器
    };

}